--[[
盲盒抽奖Lua脚本

参数说明:
KEYS:
  1: 用户抽奖次数key (user:draw:count:{userId})
  2: 奖品权重key (blindbox:weights:{boxId})
  3: 奖品库存key (blindbox:stocks:{boxId})
  4: 用户中奖记录key (user:prizes:{userId})

ARGV:
  1: 用户ID
  2: 盲盒ID
  3: 当前日期 (YYYY-MM-DD)
  4: 每日最大抽奖次数
  5: 应用标识 (用于日志追踪)

返回值:
  数组 [状态码, 消息, 奖品ID]
  状态码:
    1: 抽奖成功并中奖
    0: 抽奖失败
    -1: 抽奖次数不足
    -2: 奖品已抽完
    -3: 系统错误
]]

-- 记录日志函数
local function log(level, message)
    local logKey = "blindbox:logs:" .. ARGV[5] .. ":" .. ARGV[3]
    local logEntry = string.format("%s|%s|%s|%s|%s",
        redis.call("TIME")[1], level, ARGV[1], ARGV[2], message)
    redis.call("RPUSH", logKey, logEntry)
    redis.call("EXPIRE", logKey, 86400) -- 日志保留1天
end

-- 尝试记录日志，但不影响主流程
local function safe_log(level, message)
    local ok, err = pcall(log, level, message)
    if not ok then
        -- 记录日志失败时不中断主流程
    end
end

-- 主抽奖逻辑
local function main()

    -- 记录请求开始
    safe_log("DEBUG", "抽奖请求开始")
    redis.pcall("publish",'log',"抽奖请求开始")

    local userId = ARGV[1]
    local boxId = ARGV[2]
    local today = ARGV[3]
    local dailyLimit = tonumber(ARGV[4])

    local userKey = KEYS[1]
    local weightKey = KEYS[2]
    local stockKey = KEYS[3]
    local prizeListKey = KEYS[4]

    -- 1. 检查用户抽奖次数
    redis.pcall("publish",'log',"新用户初始化开始1" ..userKey)
    local remain = tonumber(redis.call("HGET", userKey, today))
    redis.pcall("publish",'log',"新用户初始化开始2")
    if remain == nil then

        -- 新用户初始化次数
        redis.call("HSET", userKey, today, dailyLimit)

        remain = dailyLimit
        safe_log("INFO", "新用户初始化次数: " .. dailyLimit)
        redis.pcall("publish",'log',"新用户初始化次数" ..remain)
    end

    if remain <= 0 then
        safe_log("WARN", "抽奖次数不足")
        redis.pcall("publish",'log',"抽奖次数不足")
        return -1
    end

    -- 扣减抽奖次数
    redis.call("HINCRBY", userKey, today, -1)
    safe_log("DEBUG", "扣减后剩余次数: " .. (remain - 1))
    redis.pcall("publish",'log',"扣减后剩余次数"..(remain - 1))
    -- 2. 获取奖品权重集合
    local weights = redis.call("ZRANGE", weightKey, 0, -1, "WITHSCORES")
    if #weights == 0 then
        safe_log("ERROR", "奖品权重集合为空")
        redis.pcall("publish",'log',"奖品权重集合为空")
        return -2
    end

    -- 3. 计算总权重
    local totalWeight = 0
    for i = 2, #weights, 2 do
        totalWeight = totalWeight + tonumber(weights[i])
    end

    if totalWeight <= 0 then
        safe_log("ERROR", "总权重为0")
        redis.pcall("publish",'log',"总权重为0")
        return -2
    end

    -- 4. 生成随机数（使用Redis的随机种子）
    local timestamp = redis.call("TIME")
    math.randomseed(timestamp[1] * 1000000 + timestamp[2])
    local random = math.random(1, totalWeight)
    safe_log("DEBUG", "随机数生成: " .. random .. "/" .. totalWeight)
    redis.pcall("publish",'log',"随机数生成".. random .. "/" .. totalWeight)
    -- 5. 遍历奖品选择
    local selectedPrize = ""
    local currentWeight = 0

    for i = 1, #weights, 2 do
        local prizeId = weights[i]
        local weight = tonumber(weights[i+1])
        currentWeight = currentWeight + weight

        if random <= currentWeight then
            -- 6. 检查库存
            local stock = tonumber(redis.call("HGET", stockKey, prizeId))
            if not stock then
                safe_log("ERROR", "奖品库存不存在: " .. prizeId)
                redis.pcall("publish",'log',"奖品库存不存在" .. prizeId)
                break
            end

            if stock > 0 then
                -- 7. 扣减库存
                local newStock = redis.call("HINCRBY", stockKey, prizeId, -1)
                if newStock >= 0 then
                    selectedPrize = prizeId
                    safe_log("INFO", "抽中奖品: " .. prizeId .. ", 新库存: " .. newStock)
                    redis.pcall("publish",'log',"抽中奖品: " .. prizeId .. ", 新库存: " .. newStock)
                    -- 8. 记录中奖信息
                    local drawTime = timestamp[1] .. "." .. string.format("%06d", timestamp[2])
                    local prizeRecord = boxId .. ":" .. prizeId .. ":" .. drawTime
                    redis.call("RPUSH", prizeListKey, prizeRecord)
                    redis.call("EXPIRE", prizeListKey, 2592000) -- 30天有效期

                    -- 9. 返回中奖结果
                    return 1
                else
                    -- 库存扣减后为负，恢复库存
                    redis.call("HINCRBY", stockKey, prizeId, 1)
                    safe_log("WARN", "库存扣减失败: " .. prizeId .. ", 恢复库存")
                    redis.pcall("publish",'log',"库存扣减失败: " .. prizeId .. ", 恢复库存")
                    -- 从权重集合中移除该奖品
                    redis.call("ZREM", weightKey, prizeId)
                    safe_log("INFO", "从奖品池移除: " .. prizeId)
                    redis.pcall("publish",'log',"从奖品池移除: " .. prizeId)
                end
            else
                safe_log("WARN", "奖品库存不足: " .. prizeId)
                redis.pcall("publish",'log',"奖品库存不足: " .. prizeId)
                -- 从权重集合中移除该奖品
                redis.call("ZREM", weightKey, prizeId)
                safe_log("INFO", "从奖品池移除: " .. prizeId)
                redis.pcall("publish",'log',"从奖品池移除: " .. prizeId)
            end
            break
        end
    end

    safe_log("WARN", "未抽中任何奖品")
    redis.pcall("publish",'log',"未抽中任何奖品")
    return 0
end

-- 异常处理
local ok, result = pcall(main)
if not ok then
    safe_log("ERROR", "执行错误: " .. tostring(result))
    redis.pcall("publish",'log',"执行错误: " .. tostring(result))
    return -3
end

return result